package com.online.pay.common.sign;

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.online.pay.common.modle.WxContains;
import com.online.pay.common.modle.WxContains.SignType;

/**
 * 微信签名验签工具类
 * <p>https://suijimimashengcheng.51240.com/</p>
 * <p>注：key为商户平台设置的密钥key</p>
 * @author Qiurz
 *
 */
public class WxPaySignUtil {

	/**
	 * 验签
	 * @param data 待验签数据
	 * @param key 秘钥 
	 * @return 验签是否正确
	 * @throws Exception
	 */
	public static boolean verify(Map<String, String> data, String key) throws Exception {
		return verify(data, key,SignType.MD5);
	}

	/**
	 * 验签
	 * @param data 待处理数据
	 * @param key 秘钥
	 * @param signType 类型
	 * @return
	 * @throws Exception
	 */
	public static boolean verify(Map<String, String> data, String key, SignType signType) throws Exception {
		if (!data.containsKey(WxContains.SIGN)) {
			return false;
		}
		String sign = data.get(WxContains.SIGN);
		return sign(data, key, signType).equals(sign);
	}

	/**
	 * 签名
	 * 
	 * @param data
	 *            待签名数据
	 * @param key
	 *            秘钥
	 * @return 签名串
	 * @throws Exception
	 */
	public static String sign(Map<String, String> data, String key) throws Exception {
		return sign(data, key, SignType.MD5);
	}

	/**
	 * 签名
	 * 
	 * @param data
	 *            待处理数据
	 * @param key
	 *            秘钥
	 * @param signType
	 *            签名类型
	 * @return 签名串
	 * @throws Exception
	 */
	public static String sign(Map<String, String> data, String key, SignType signType) throws Exception {
		Set<String> ky = data.keySet();
		// 转数组
		String[] keyArray = ky.toArray(new String[ky.size()]);
		// 排序
		Arrays.sort(keyArray);
		StringBuffer sb = new StringBuffer();
		for (String k : keyArray) {
			// 当时sign标签时返回继续遍历
			if (k.equals(WxContains.SIGN)) {
				continue;
			}
			// 去除参数空值,不参与签名
			if (data.get(k).trim().length() > 0) {
				sb.append(k).append("=").append(data.get(k).trim()).append("&");
			}
		}
		sb.append("key=").append(key);
		if (SignType.MD5.equals(signType)) {
			return MD5(sb.toString().toUpperCase());
		} else if (SignType.HMACSHA256.equals(signType)) {
			return HMACSHA256(sb.toString().toUpperCase(), key);
		} else {
			throw new RuntimeException(String.format("无效的sign_type: %s", signType));
		}
	}

	/**
	 * 生成HMACSHA256
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	private static String HMACSHA256(String data, String key) throws Exception {
		Mac mac = Mac.getInstance(WxContains.HMACSHA256);
		SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), WxContains.HMACSHA256);
		mac.init(keySpec);
		byte[] bs = mac.doFinal(data.getBytes("UTF-8"));
		StringBuilder sb = new StringBuilder();
		for (byte b : bs) {
			sb.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1, 3));
		}
		return sb.toString().toUpperCase();
	}

	/**
	 * 生成MD5
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	private static String MD5(String data) throws Exception {
		MessageDigest messageDigest = MessageDigest.getInstance(WxContains.MD5);
		byte[] bs = messageDigest.digest(data.getBytes("UTF-8"));
		StringBuilder sb = new StringBuilder();
		for (byte b : bs) {
			sb.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1, 3));
		}
		return sb.toString().toUpperCase();
	}

}
